using System;
using System.Collections;
using Castle.Core;
using Castle.Windsor;
using RomanJendrusz.Amazon.ETL.Infrastructure.IoC;
using RomanJendrusz.Amazon.ETL.Infrastructure.IoC.Windsor;

namespace RomanJendrusz.Amazon.ETL.Infrastructure.IoC.Windsor
{
    public class WindsorDependencyResolverAdapter : IDependencyResolver
    {
        private IWindsorContainer container;

        public WindsorDependencyResolverAdapter(IWindsorInitializer initializer)
        {
            if (initializer == null) throw new ArgumentNullException("initializer");
            init(initializer);
        }

        #region IDependencyResolver Members

        /// <summary>
        /// Resolves the specified type of object.
        /// </summary>
        /// <typeparam name="Dependency">The type of the Dependency.</typeparam>
        /// <returns></returns>
        public Dependency Resolve<Dependency>()
        {
            Dependency dependency;
            try
            {
                dependency = container.Resolve<Dependency>();
            }
            catch(Exception ex)
            {
                string message = string.Format("Could not resolve dependency of type {0}", typeof (Dependency));
                throw new IoCException(message, ex);
            }
            return dependency;
        }

        public Object Resolve(Type service)
        {
            object dependency;
            try
            {
                dependency = container.Resolve(service);
            }
            catch (Exception ex)
            {
                string message = string.Format("Could not resolve dependency of type {0}", service);
                throw new IoCException(message, ex);
            }
            return dependency;
        }

        /// <summary>
        /// Resolves the specified type of object with key.
        /// </summary>
        /// <typeparam name="Dependency">The type of the Dependency.</typeparam>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        public Dependency Resolve<Dependency>(string key)
        {
            if (key == null) throw new ArgumentNullException("key");
            Dependency dependency;
            try
            {
                dependency = container.Resolve<Dependency>(key);
            }
            catch (Exception ex)
            {
                string message = string.Format("Could not resolve dependency of type {0}", typeof(Dependency));
                throw new IoCException(message, ex);
            }
            return dependency;
        }

        /// <summary>
        /// Resolves the specified type of object with arguments.
        /// </summary>
        /// <typeparam name="Dependency">The type of the dependency.</typeparam>
        /// <param name="arguments">The arguments.</param>
        /// <returns></returns>
        public Dependency Resolve<Dependency>(IDictionary arguments)
        {
            if (arguments == null) throw new ArgumentNullException("arguments");
            Dependency dependency;
            try
            {
                dependency = container.Resolve<Dependency>(arguments);
            }
            catch (Exception ex)
            {
                string message = string.Format("Could not resolve dependency of type {0}", typeof(Dependency));
                throw new IoCException(message, ex);
            }
            return dependency;
        }

        public void AddComponent<OfType>()
        {
            container.AddComponentWithLifestyle<OfType>(LifestyleType.Transient);
        }

        public void AddComponent<OfType>(string key)
        {
            container.AddComponentWithLifestyle<OfType>(key, LifestyleType.Transient);
        }

        public void AddComponent<Service, Implementation>() where Implementation : class
        {
            container.AddComponentWithLifestyle<Service, Implementation>(LifestyleType.Transient);
        }

        public void AddComponent<Service, Implementation>(string key) where Implementation : class
        {
            container.AddComponentWithLifestyle<Service, Implementation>(key, LifestyleType.Transient);
        }

        public void AddComponent<OfType>(LifeStyle lifestyle)
        {
            switch(lifestyle)
            {
                case LifeStyle.Singletone:
                    container.AddComponentWithLifestyle<OfType>(LifestyleType.Singleton);
                    break;
                case LifeStyle.Thread:
                    container.AddComponentWithLifestyle<OfType>(LifestyleType.Thread);
                    break;
                case LifeStyle.WebRequest:
                    container.AddComponentWithLifestyle<OfType>(LifestyleType.PerWebRequest);
                    break;
                case LifeStyle.Transient:
                    container.AddComponentWithLifestyle<OfType>(LifestyleType.Transient );
                    break;

            }
        }

        public void AddComponent<OfType>(string key, LifeStyle lifestyle)
        {
            switch (lifestyle)
            {
                case LifeStyle.Singletone:
                    container.AddComponentWithLifestyle<OfType>(key, LifestyleType.Singleton);
                    break;
                case LifeStyle.Thread:
                    container.AddComponentWithLifestyle<OfType>(key, LifestyleType.Thread);
                    break;
                case LifeStyle.WebRequest:
                    container.AddComponentWithLifestyle<OfType>(key, LifestyleType.PerWebRequest);
                    break;
                case LifeStyle.Transient:
                    container.AddComponentWithLifestyle<OfType>(key, LifestyleType.Transient);
                    break;
            }
        }

        public void AddComponent<Service, Implementation>(LifeStyle lifestyle) where Implementation:class 
        {
            switch (lifestyle)
            {
                case LifeStyle.Singletone:
                    container.AddComponentWithLifestyle<Service, Implementation>(LifestyleType.Singleton);
                    break;
                case LifeStyle.Thread:
                    container.AddComponentWithLifestyle<Service, Implementation>(LifestyleType.Thread);
                    break;
                case LifeStyle.WebRequest:
                    container.AddComponentWithLifestyle<Service, Implementation>(LifestyleType.PerWebRequest);
                    break;
                case LifeStyle.Transient:
                    container.AddComponentWithLifestyle<Service, Implementation>(LifestyleType.Transient);
                    break;

            }
        }

        public void AddComponent<Service, Implementation>(IDictionary parameters) where Implementation:class
        {
            container.AddComponentWithLifestyle<Service, Implementation>(parameters);
        }

        public void AddComponent<Service, Implementation>(string key, LifeStyle lifestyle) where Implementation : class
        {
            switch (lifestyle)
            {
                case LifeStyle.Singletone:
                    container.AddComponentWithLifestyle<Service, Implementation>(key ,LifestyleType.Singleton);
                    break;
                case LifeStyle.Thread:
                    container.AddComponentWithLifestyle<Service, Implementation>(key ,LifestyleType.Thread);
                    break;
                case LifeStyle.WebRequest:
                    container.AddComponentWithLifestyle<Service, Implementation>(key ,LifestyleType.PerWebRequest);
                    break;
                case LifeStyle.Transient:
                    container.AddComponentWithLifestyle<Service, Implementation>(key, LifestyleType.Transient);
                    break;

            }
        }

        public void AddInstance<Type>(Type injectedInstance)
        {
            container.Kernel.AddComponentInstance<Type>(typeof(Type), injectedInstance);
        }

        public void AddInstance<Type>(string key, Type injectedInstance)
        {
            throw new NotSupportedException();
        }

        #endregion

        private void init(IWindsorInitializer initializer)
        {
            container = initializer.CreateWindsorContainer();
            if(container == null)
            {
                throw new IoCException("Windsor container was not created");
            }
        }

        public IWindsorContainer AdoptedContainer
        {
            get { return container; }
        }
    }
}